
package com.scalar.chiptrack.proxy;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;

/**
* ProxySocket listens for a connection on the specified TCP port, then makes
* a connection to another IpAddress and port number. It then starts two
* threads to handle bidirectional traffic on the two sockets. It then loops
* back to waiting for more connections.
*
*/

public class ProxySocket extends Thread {
    
    private int iListenPortNumber ;
    private InetAddress IpAddr ;
    private int iDestPortNumber ;
    private String sDestIpAddress ;
    protected Socket soInbound, soOutbound ;
    private SocketIoBase sOutbound, sInbound ;
    private ServerSocket soServer ;
    protected int iLogging ;
    private Logger Log ;
    private int iConn, iConnCount, iDataCapture ;
    private String MyName ;
    private FileOutputStream dcRw = null ;

    public ProxySocket (int iListenPortNumber,
                        String sDestIpAddress,
                        int iDestPortNumber,
                        Logger Log,
                        int iLogging,
                        int iDataCapture,
                        String MyName) {

        this.iListenPortNumber = iListenPortNumber ;
        this.Log = Log ;
        this.iLogging = iLogging ;
        this.iDataCapture = iDataCapture ;
        try {
            this.IpAddr = InetAddress.getByName(sDestIpAddress) ;
        } 
        catch (UnknownHostException e) {}

        this.iDestPortNumber = iDestPortNumber ;
        this.sDestIpAddress = sDestIpAddress ;
        this.MyName = MyName ;
    }

    public void run() {
        int iEcho = 0 ;

        this.setName(MyName) ;

        // create a listen socket

        if(iLogging > 0) {
            Log.It("Creating a listen socket", iLogging) ;
        }

        try {
            soServer = new ServerSocket(iListenPortNumber) ;
        } 
        catch(IOException e) {}

        iConn = 0 ;
        iConnCount = 0 ;

        InputStream soInboundInputStream = null;
        OutputStream soOutboundOutputStream = null;
        char[] unread = null;
        while(true) {
            // listen & accept an incoming connection
            try {
                if(iLogging > 0) {
                    Log.It("Listening on port " + iListenPortNumber + " ...", iLogging) ;
                }

                soInbound = soServer.accept() ;

                if(iLogging > 0) {
                    Log.It("Accepted a connection from " + soInbound.getInetAddress() + ":" + soInbound.getPort(), iLogging) ;
                }

                /*
                ///////////// Get destination host and port /////////////////////////////////////////
                
                soInboundInputStream = soInbound.getInputStream();
                BufferedReader in = new BufferedReader (new InputStreamReader (soInboundInputStream), 256);
                StringBuffer buf = new StringBuffer();
                char ch = (char)-1;
                int i = 0;
                while (true) {
                    ch = (char)in.read();
                    if ((ch == '\r') || (ch == '\n')) {
                        break;
                    }
                    buf.append(ch);
                    i++;
                }
                String firstLine = buf.toString() + "\r\n";
                unread = firstLine.toCharArray();
                //in.unread (unread); // push it back
                
                StringTokenizer st = new StringTokenizer (firstLine);
                st.nextToken(); 
                st = new StringTokenizer (st.nextToken().substring(6), "/");
                
                sDestIpAddress = st.nextToken();
                iDestPortNumber = 80;
                
                int colonIndex = sDestIpAddress.indexOf(':');
                if (colonIndex != -1) {
                    iDestPortNumber = Integer.parseInt(sDestIpAddress.substring (colonIndex+1));
                    sDestIpAddress = sDestIpAddress.substring (0, colonIndex);
                }
                
                System.out.println ("sDestIpAddress = " + sDestIpAddress + " iDestPortNumber = " + iDestPortNumber);
                
                /////////////////////////////////////////////////////////////////////////////////////
                */
            } 
            catch(IOException e) {
                Log.It("Error on accept " + e, iLogging) ;
                continue ;
            }

            /*
            if(iLogging > 0) {
                Log.It("Connecting to " + sDestIpAddress + ":" + iDestPortNumber, iLogging) ;
            }

            
            if(iEcho > 0) {
                // loopback the two sockets
                soOutbound = soInbound ;
            }
            else {
                // make a socket connection to destination
                try {
                    soOutbound = new Socket(IpAddr, iDestPortNumber) ;
                    soOutboundOutputStream = soOutbound.getOutputStream();
                    BufferedWriter out = new BufferedWriter (new OutputStreamWriter(soOutboundOutputStream));
                    out.write (unread);
                    
                } 
                catch(IOException e) {
                    Log.It("Error on connect " + e, iLogging) ;
                    try {
                        soInbound.close() ;
                    } 
                    catch(IOException ee) {}

                    soInbound = null ;
                    continue ;
                }
            }*/

            // pass startup parameters to socket handling classes
            if(iLogging > 0) {
                Log.It("Setting up socket handler class data", iLogging) ;
            }

            // create two new standalone socket handler threads

            sOutbound = new SocketIoBase() ;
            //sInbound = new SocketIoBase() ;

            iConn++ ;
            iConnCount++ ;

            if(iLogging > 0) {
                Log.It("Current proxy connections: " + iConn, iLogging) ;
                Log.It("Total proxy connections: " + iConnCount, iLogging) ;
            }

            if(iDataCapture > 0) {
                try {
                    dcRw = new FileOutputStream(MyName + "_" + iConnCount + ".cap") ;
                } 
                catch(IOException e) {
                    Log.It("Problem opening data capture files:" + e, iLogging) ;
                    iDataCapture = 0 ;
                }
            }
            
            // setup sockets and logging for the socket handler threads
            sOutbound.Setup (this, soInbound, /*soOutbound*/null, MyName + "OrI" + iConnCount, Log,
                             iLogging, iDataCapture, dcRw, "OrI>") ;

            /*sInbound.Setup (this, soOutbound, soInbound, MyName + "NeW" + iConnCount,
                            Log, iLogging, iDataCapture, dcRw, "NeW>") ;

            // pass peer thread to each other
            sOutbound.SetPeer (sInbound) ;
            sInbound.SetPeer (sOutbound) ;

            // start them up and running
            if(iLogging > 0) {
                Log.It("Starting up socket handlers", iLogging) ;
            }
            */
            sOutbound.start();
            //sInbound.start();

            // free my sockets and go back to the accept
            soOutbound = null;
            soInbound = null;
            dcRw = null;
        }
    }

    
    synchronized void ConnectionLost() {
        // socket handler thread that lost the socket will call this method
        // to update current connection counts.
        iConn-- ;
    }
}